using hlib.IO;
using QntPlatform.Strategy;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace QntPlatform.Strategy
{
    public class TvCmdHandler : StrategyBase
    {
        ILog log = Log.Default();
        public TvCmdHandler(IExchange exchange, ISysFun sysFun, ISysApi sysApi, ITAFun ta) : base(exchange, sysFun, sysApi, ta)
        {
            
        }
        public Dictionary<string, Action<string>> cmdFunDic = new Dictionary<string, Action<string>>();
        int trend = 0;
        decimal stopLossRatio = 0.02m;

        /** 止盈率,单倍 */
        decimal trailingProfitRatio = 0.002m; //0.005
        /** 回撤率-止盈 */
        decimal stopProfitRatio = 0.0002m; //0.001
        int leverage = 20;
        public int Trend { get => trend; set => trend = value; }

        public override void Init()
        {
            SysFun.Timer1sEvent += (p1, p2) =>
            {
                var tck = Exchange.GetTickerAsync();
                AutoSellHandle(tck.Result);
            };
            var buyStopSync = new BuyStop(this);
            buyStopSync.Init();
        }
        public override void MainFun()
        {
            //行情监测
            //命令交易
            SysFun.CommandEvent += SysFun_CommandEvent;
            //止盈止损
            cmdFunDic.Add("trend", SetTrend);
            cmdFunDic.Add("buy", Buy);
            cmdFunDic.Add("sellShort", SellShort);
            cmdFunDic.Add("ShowInfo", ShowInfo);

        }
        void Buy(string param)
        {
            if (!(Trend == 1 || Trend == 2))
            {
                return;
            }
            var cmdVal = GetCmdValue<TradeCmd>(param);
            //计算仓位
            var account = Exchange.GetAccountAsync();
            var re = Exchange.GetRecordsAsync(cmdVal.Period,30);
            var kArr = re.Result;
            var minLow = kArr.Skip(kArr.Length - 7).Min(p => p.Low); // Aggregate((p1, p2) =>(p1 == null || p1.Low > p2.Low)?p2:p1);
            var art = (decimal)TA.ATR(kArr, 14).Last();
            decimal stopLoss = minLow - art * 0.5m; //止损价
            var tck = Exchange.GetTickerAsync();
            var nowPrice = tck.Result.Buy;
            var buyAmount = account.Result.Balance * stopLossRatio / (nowPrice - stopLoss);
            //计算止盈止损
            //买卖
            var id = Exchange.BuyAsync(buyAmount);
            //止盈止损添加
            var auto = new AutoSellOrder();
            if (cmdVal.sells == null)
            {
                auto.ProfitType = AutoSellOrder.StopProfitType.Trailing;
            }
            else if (cmdVal.sells.Length == 1)
            {
                auto.ProfitType = AutoSellOrder.StopProfitType.Fixed;
            }
            else if (cmdVal.sells.Length == 2)
            {
                auto.ProfitType = AutoSellOrder.StopProfitType.TrailingGroup3;
            }
            auto.SourcePrice = nowPrice;
            auto.ProfitPrices = cmdVal.sells;
            auto.SourceOrderId = id.Result;
            auto.SourceAmount = buyAmount;
            auto.Amount = buyAmount;
            auto.Direction = SideDirection.CloseBuy;
            auto.StopLossPrice = stopLoss;
            autoSellOrders.Add(auto);
        }
        //AutoOutHandle(Ticker tck, AutoSellOrder autoSellOrder)

        internal Dictionary<string, Func<Ticker, AutoSellOrder, bool>> AutoHandlerDic = new Dictionary<string, Func<Ticker, AutoSellOrder, bool>>();
        public void AutoSellHandle(Ticker tck)
        {
            var nowBuy = tck.Buy;
            var lastPrice = tck.Last;
            var nullItemList = new List<AutoSellOrder>();

            foreach (var item in autoSellOrders)
            {
                if (item.NowState == 1)
                {
                    nullItemList.Add(item);
                    continue;
                }
                if (!string.IsNullOrWhiteSpace(item.HandlerType))
                {
                    if (AutoHandlerDic.TryGetValue(item.HandlerType, out var action))
                    {
                        if (action(tck, item))
                        {
                            item.NowState = 1;
                        }
                    }
                }
                else
                if (item.Direction == SideDirection.CloseBuy)
                {
                    stopByCloseBuy(tck, item);
                }
                else if (item.Direction == SideDirection.CloseSell)
                {
                    stopByCloseSell(tck, item);
                }
                if (item.NowState == 1)
                    nullItemList.Add(item);
            }
            if (nullItemList.Count > 0)
            {

                nullItemList.ForEach(p =>
                {
                    log.Debug($"移除：{p.Direction},{p.SourceAmount},{p.SourceOrderId}");
                    autoSellOrders.Remove(p);
                });
            }
        }
        void stopByCloseBuy(Ticker tck, AutoSellOrder item)
        {
            //检查止损
            if (tck.Buy <= item.StopLossPrice)
            {
                execTrade(item.Direction, item.Amount);
                return;
            }
            //检查止盈
            if (item.ProfitType == AutoSellOrder.StopProfitType.Fixed)
            {
                if (tck.Buy >= item.ProfitPrices[0])
                {
                    execTrade(item.Direction, item.Amount);
                    item.NowState = 1;
                    return;
                }
            }
            else if (item.ProfitType == AutoSellOrder.StopProfitType.Trailing)
            {
                //跟踪止盈
                return;
            }
            else if (item.ProfitType == AutoSellOrder.StopProfitType.TrailingGroup3)
            {
                /*
                1.止损（7根k最小值）,止盈（给定值）
                2.当达到第一止盈时：卖出1/3，止损不变
                3.当达到第二止盈时：卖出1/3，止损改为买入价
                4.等待最后1/3止损，不管止盈
                */
                if (item.ProfitStep > 1 || tck.Buy < item.ProfitPrices[item.ProfitStep])
                {
                    return;
                }
                if (item.ProfitStep == 0)
                {
                    var amount = item.Amount / 3;
                    var id = execTrade(item.Direction, amount);
                    item.ProfitStep++;
                    item.Amount = item.Amount * 2 / 3;
                    item.ProcInfo.Add($"止盈0成交,原仓位:{item.Amount},成交id:{id}");

                }
                else if (item.ProfitStep == 1)
                {
                    var amount = item.Amount / 2;
                    var id = execTrade(item.Direction, amount);
                    item.ProfitStep++;
                    item.Amount = amount;
                    item.StopLossPrice = item.SourcePrice;
                    item.ProcInfo.Add($"止盈1成交,原仓位:{item.Amount},成交id:{id},止损变更:{item.StopLossPrice}");
                }
                else
                {
                    throw new QntException("无法处理的步骤", item);
                }
                log.Debug("止盈成交：", $"{item.SourceOrderId},${item.Amount},{item.ProcInfo.Aggregate((p1, p2) => p1 + "," + p2)}");
            }
        }
        void stopByCloseSell(Ticker tck, AutoSellOrder item)
        {
            //检查止损
            if (calcUnitProfit(tck.Buy, item.StopLossPrice, false) <= 0)
            {

                execTrade(item.Direction, item.Amount);
                return;
            }
            //检查止盈
            if (item.ProfitType == AutoSellOrder.StopProfitType.Fixed)
            {
                if (tck.Buy <= item.ProfitPrices[0])
                {
                    execTrade(item.Direction, item.Amount);
                    item.NowState = 1;
                    return;
                }
            }
            else if (item.ProfitType == AutoSellOrder.StopProfitType.Trailing)
            {
                //跟踪止盈
                return;
            }
            else if (item.ProfitType == AutoSellOrder.StopProfitType.TrailingGroup3)
            {
                /*
                1.止损（7根k最小值）,止盈（给定值）
                2.当达到第一止盈时：卖出1/3，止损不变
                3.当达到第二止盈时：卖出1/3，止损改为买入价
                4.等待最后1/3止损，不管止盈
                */
                if (item.ProfitStep > 1 || calcUnitProfit(tck.Buy, item.ProfitPrices[item.ProfitStep], false) < 0)
                {
                    return;
                }
                if (item.ProfitStep == 0)
                {
                    var amount = item.Amount / 3;
                    var id = execTrade(item.Direction, amount);
                    item.ProfitStep++;
                    item.Amount = item.Amount * 2 / 3;
                    item.ProcInfo.Add($"止盈0成交,原仓位:{item.Amount},成交id:{id}");

                }
                else if (item.ProfitStep == 1)
                {
                    var amount = item.Amount / 2;
                    var id = execTrade(item.Direction, amount);
                    item.ProfitStep++;
                    item.Amount = amount;
                    item.StopLossPrice = item.SourcePrice;
                    item.ProcInfo.Add($"止盈1成交,原仓位:{item.Amount},成交id:{id}");
                }
                else
                {
                    throw new QntException("无法处理的步骤", item);
                }
                log.Debug("止盈成交：", item);
            }
        }
        /** <summary>单位利润
         @param {Number} nowPrice 当前价
      @param {Number} buyPrice 买入价
      @param {Boolean} direction 是否多单，默认多单
         * </summary>
     * <param name="direction">是否多单，默认多单</param>
     */
        decimal calcUnitProfit(decimal nowPrice, decimal buyPrice, bool direction = true)
        {
            return direction ? nowPrice - buyPrice : -(nowPrice - buyPrice);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="amount"></param>
        /// <returns></returns>
        internal IConvertible execTrade(SideDirection direction, decimal amount)
        {
            log.Debug("autoSellOrders:", autoSellOrders);
            IConvertible id = null;
            if (direction == SideDirection.CloseBuy)
            {
                id = Exchange.Sell_CloseBuyAsync(-1, amount).Result;
            }
            else if (direction == SideDirection.CloseSell)
            {
                id = Exchange.Buy_CloseSellAsync(-1, amount).Result;
            }
            else
            {
                throw new QntException("无效操作方向", new { direction, amount });
            }
            if (id == null)
            {
                throw new QntException("交易失败", new { direction, amount });
            }
            log.Debug("交易信息：", new { direction, amount, id });
            return id;
        }

        void SellShort(string param)
        {
            if (!(Trend == -1 || Trend == 2))
            {
                log.Debug("趋势不符，忽略交易命令", new { Trend });
                return;
            }
            var cmdVal = GetCmdValue<TradeCmd>(param);
            //计算仓位
            var account = Exchange.GetAccountAsync();
            var ret = Exchange.GetRecordsAsync(cmdVal.Period);
            var kArr = ret.Result;
            var max = kArr.Skip(kArr.Length - 7).Max(p => p.Low); // Aggregate((p1, p2) =>(p1 == null || p1.Low > p2.Low)?p2:p1);
            var art = (decimal)TA.ATR(kArr, 14).Last();
            var stopLoss = max + art * 0.5m; //止损价
            var tck = Exchange.GetTickerAsync();
            var nowPrice = tck.Result.Buy;
            var buyAmount = account.Result.Balance * stopLossRatio / (stopLoss - nowPrice);
            //计算止盈止损
            //买卖
            var id = Exchange.SellAsync(-1, buyAmount);
            //止盈止损添加
            var auto = new AutoSellOrder();
            if (cmdVal.sells == null)
            {
                auto.ProfitType = AutoSellOrder.StopProfitType.Trailing;
            }
            else if (cmdVal.sells.Length == 1)
            {
                auto.ProfitType = AutoSellOrder.StopProfitType.Fixed;
            }
            else if (cmdVal.sells.Length == 2)
            {
                auto.ProfitType = AutoSellOrder.StopProfitType.TrailingGroup3;
            }
            auto.SourcePrice = nowPrice;
            auto.ProfitPrices = cmdVal.sells;
            auto.SourceOrderId = id.Result;
            auto.SourceAmount = buyAmount;
            auto.Amount = buyAmount;
            auto.Direction = SideDirection.CloseSell;
            auto.StopLossPrice = stopLoss;
            autoSellOrders.Add(auto);
        }
        void SetTrend(string param)
        {
            var val = GetCmdValue<int>(param);
            if (val == 1 || val == 2 || val == -1)
            {
                Trend = val;
            }
        }
        private void SysFun_CommandEvent(object sender, string e)
        {
            log.Debug(e);
            var cmd = GetCmdKV(e);
            if (cmdFunDic.TryGetValue(cmd.Key, out var funVal))
            {
                funVal(cmd.Value);
            }
        }

        public KeyValuePair<string, string> GetCmdKV(string cmdStr)
        {

            var cmdArr = cmdStr.Split(':', 2);
            string cmdVal = null;
            var cmdName = cmdArr[0];
            if (cmdArr.Length < 2)
            {
                if (cmdStr.StartsWith('{') || cmdStr.EndsWith('}'))
                {
                    var dic = SysFun.ParseTo<Dictionary<string, object>>(cmdStr);
                    if (dic.TryGetValue("cmdName", out var strName))
                    {
                        return new KeyValuePair<string, string>(strName + "", cmdStr);
                    }
                }
                return new KeyValuePair<string, string>(cmdName, cmdVal);
            }
            // var cr = new CsvReader(new StringReader(cmdStr),'~','*') ;
            //var list= cr.ReadCsv();


            cmdVal = cmdArr[1];
            return new KeyValuePair<string, string>(cmdName, cmdVal);
        }
        public void ShowInfo(string str)
        {
            log.Debug("autoSellOrders", autoSellOrders);
        }
        public T GetCmdValue<T>(string cmdValStr)
        {
            return SysFun.ParseTo<T>(cmdValStr);
        }
        internal List<AutoSellOrder> autoSellOrders = new List<AutoSellOrder>();
        /// <summary>
        /// 交易命令
        /// </summary>
        public class TradeCmd
        {
            public decimal? Price { get; set; }
            public decimal[] sells { get; set; }
            public int Period { get; set; }
            //    public SideDirection Direction { get; set; }
        }
        public class AutoSellOrder
        {
            public AutoSellOrder() {
                ProcInfo = new List<string>();
            }
            /// <summary>
            /// 1已完成
            /// </summary>
            public int NowState { get; set; }
            public IConvertible SourceOrderId { get; set; }
            public SideDirection Direction { get; set; }
            public decimal Amount { get; set; }
            public decimal StopLossPrice { get; set; }
            public decimal SourcePrice { get; set; }
            public decimal[] ProfitPrices { get; set; }
            public int ProfitStep { get; set; }
            /// <summary>
            /// 最大收益率
            /// </summary>
            public decimal TopProfitRatio { get; set; }
            /// <summary>
            /// 最大亏损率,负值
            /// </summary>
            public decimal LowProfitRatio { get; set; }
            public StopProfitType ProfitType { get; set; }
            public List<string> ProcInfo { get; }
            public decimal SourceAmount { get; internal set; }
            public string HandlerType { get; set; }

            //public dynamic ExtInfo { get; }

            public enum StopProfitType
            {
                Fixed,
                Trailing,
                TrailingGroup3
            }
        }


    }
}
